﻿
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using System;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using Nirvana;
using System.Reflection;
using UnityEditor;
using Nirvana.UI;


/// <summary>
/// UI回调
/// </summary>
/// <param name="objs"></param>
public delegate void UICallBack(UIWindowBase UI);
public delegate void UICallBack<T>(T UI);
public delegate void UIAnimCallBack(UIWindowBase UIbase, UICallBack callBack);

/// <summary>
/// UI框架管理器：控制每层的UI开启，关闭等状态
/// </summary>
[RequireComponent(typeof(UIStackManager))]
[RequireComponent(typeof(UILayerManager))]
[RequireComponent(typeof(UIAnimManager))]
public class UIManager : MonoBehaviour
{
    private static UIManager Inst;
    /// <summary>
    /// 所有UI模块的命名空间
    /// </summary>
    private const string AssemblyUI = "Nirvana.UI";
    private MethodInfo AddComponentMethodInfo;

    private  GameObject s_UIManagerGo;

    private  UILayerManager s_UILayerManager; //UI层级管理器
    private  UIAnimManager s_UIAnimManager;   //UI动画管理器
    private  UIStackManager s_UIStackManager; //UI栈管理器

    private  EventSystem s_EventSystem;
    //打开的UI
    public  Dictionary<string, UIWindowBase> s_UIs = new Dictionary<string, UIWindowBase>();
    //隐藏的UI
    public  Dictionary<string, UIWindowBase> s_hideUIs = new Dictionary<string, UIWindowBase>();

    private HashSet<string> s_WaitLoadUIs = new HashSet<string>();

    private Queue<UIPoolEvent> s_UIPoolEventQueue = new Queue<UIPoolEvent>();

    private struct UIPoolEvent
    {
        public string modulePath;
        public string uiName;
        public UIType uiType;
        public UIWindowBase.WindowAnimType windowAnimType;
        public UICallBack complete;
        public UIPoolEvent(string modulePath,string uiName, UIType uitype, UIWindowBase.WindowAnimType windowAnimType,UICallBack complete) 
        {
            this.modulePath = modulePath;
            this.uiName = uiName;
            this.uiType = uitype;
            this.windowAnimType = windowAnimType;
            this.complete = complete;
        }
    }
    private bool s_IsLoading = false;
    private bool s_IsLoadWait = false;
    private bool s_IsLadWaitOK = false;
    /// <summary>
    /// 存放不受其它窗口影响的UI：避免新窗口打开把它关闭了
    /// </summary>
    private string[] s_IngorePauseUIs = new string[] { "MainSceneTopUI" };

    #region 初始化

    private static bool isInit = false;
    
    [RuntimeInitializeOnLoadMethod]
    public static void Init()
    {
        if (!isInit)
        {
            if (Application.platform == RuntimePlatform.WindowsEditor)
            {
                if (!Application.isPlaying)
                    return;
            }
            GameObject instance = GameObject.Find("UIManager");

            if (instance == null)
            {
                var prefab = Resources.Load<GameObject>("UI/UIManager");
                instance = Instantiate<GameObject>(prefab);
                instance.transform.localPosition = Vector3.zero;
                instance.transform.localScale = Vector3.one;
            }

            isInit = true;
        }
    }


    public static UILayerManager UILayerManager
    {
        get
        {
            return Inst.s_UILayerManager;
        }

        set
        {
            Inst.s_UILayerManager = value;
        }
    }


    public static UIAnimManager UIAnimManager
    {
        get
        {
            return Inst.s_UIAnimManager;
        }

        set
        {
            Inst.s_UIAnimManager = value;
        }
    }

    public static UIStackManager UIStackManager
    {
        get
        {
            return Inst.s_UIStackManager;
        }

        set
        {

            Inst.s_UIStackManager = value;
        }
    }

    public static EventSystem EventSystem
    {
        get
        {
            return Inst.s_EventSystem;
        }

        set
        {
            Inst.s_EventSystem = value;
        }
    }

    private void Awake()
    {
        Inst = this;
        s_UIManagerGo = gameObject;
    }

    private void Start()
    {
        s_IsLoading = false;
        s_IsLoadWait = false;
        s_IsLadWaitOK = false;
        s_UILayerManager = s_UIManagerGo.GetComponent<UILayerManager>();
        s_UIAnimManager = s_UIManagerGo.GetComponent<UIAnimManager>();
        s_UIStackManager = s_UIManagerGo.GetComponent<UIStackManager>();
        s_EventSystem = EventSystem.current;
        GetCamera().gameObject.tag = "UICamera";
        InsterToURPCameraStack();
        DontDestroyOnLoad(s_UIManagerGo);
    }

    private void Update()
    {
        if (s_IsLoading) return;

        if(s_UIPoolEventQueue.Count > 0)
        {
            if (!s_IsLoadWait) //先加载Loading界面，避免点击触发其他UI
            {
                s_IsLoadWait = true;
                s_IsLadWaitOK = false;
                CreateLoadingWait();
            }

            s_IsLoading = true;
            var item = s_UIPoolEventQueue.Dequeue();
            LoadUIOnQueue(item);
        }
        else
        {
            if (s_IsLoadWait && s_IsLadWaitOK) //直到所有UI队列都加载完成，才关闭Loading
            {
                s_IsLoadWait = false;
                s_IsLadWaitOK = false;
                CloseUIWindow("LoadingWaitWindow");
            }
        }
    }


    private void LoadUIOnQueue(UIPoolEvent item)
    {
        try
        {
            CreateUIWindow(item.modulePath, item.uiName, item.uiType, item.windowAnimType, (UIbase) =>
            {
                OnLoadComplete(UIbase, item.complete);

                s_WaitLoadUIs.Remove(UIbase.UIName);
                s_IsLoading = false;
            });
        }
        catch(Exception e)
        {
            Debug.LogErrorFormat("LoadUIOnQueue :{0} ", e.Message);
            s_WaitLoadUIs.Remove(item.uiName);
            s_IsLoading = false;
        }
        

    }

    private void OnLoadComplete(UIWindowBase UIbase, UICallBack callback)
    {
        RemoveHideUI(UIbase);
        AddUI(UIbase);

        //Fixed图层和Top图层不关闭上一级UI
        if (UIbase.m_UIType != UIType.Fixed && UIbase.m_UIType != UIType.Top)
        {
            var lastUI = UIStackManager.GetLastUI(UIbase.m_UIType);
            if (lastUI != null)
            {
                bool isIngorePause = false;
                foreach (var s in s_IngorePauseUIs) //排除对应图层中，是否有忽略关闭的UI窗口
                {
                    if (lastUI.UIName.Equals(s))
                    {
                        isIngorePause = true;
                        break;
                    }
                }
                if (!isIngorePause) UIAnimManager.StartPauseAnim(lastUI, null);
            }
        }

        UIStackManager.AddUIStack(UIbase);

        UIbase.windowStatus = UIWindowBase.WindowStatus.OpenAnim;
        UISystemEvent.Dispatch(UIbase, UIEvent.OnOpen);  //派发OnOpen事件

        try
        {
            UIbase.OnOpen();
        }
        catch (Exception e)
        {
            Debug.LogErrorFormat("{0} OnOpen Exception: {1} ", UIbase.UIName, e.ToString());
        }

        if (UIbase.windowAnimType != UIWindowBase.WindowAnimType.None)
        {
            UIAnimManager.StartEnterAnim(UIbase, callback); //播放动画
        }
        else
        {
            ShowUI(UIbase);
            try
            {
                callback?.Invoke(UIbase);
            }
            catch (Exception e)
            {
                Debug.LogError(e.ToString());
            }
        }
    }

    private void CreateLoadingWait()
    {
        var loadingwaitOnHide = GetHideUI<LoadingWaitWindow>();
        var loadingwaitOnShow = GetUI<LoadingWaitWindow>();
        if(loadingwaitOnHide != null)
        {
            s_IsLadWaitOK = true;
            OnLoadComplete(loadingwaitOnHide, null);
            return;
        }
        if(loadingwaitOnShow != null)
        {
            s_IsLadWaitOK = true;
            OnLoadComplete(loadingwaitOnShow, null);
            return;
        }

        CreateUIWindow("UI", "LoadingWaitWindow", UIType.Top, UIWindowBase.WindowAnimType.None, (UIbase) => {
            OnLoadComplete(UIbase, null);
            s_IsLadWaitOK = true;
        });
    }

    private void OnDestroy()
    {
        Inst = null;
        isInit = false;
        AddComponentMethodInfo = null;
        s_IsLoading = false;
        s_IsLoadWait = false;
        s_IsLadWaitOK = false;
        s_UILayerManager = null;
        s_UIAnimManager = null;
        s_UIStackManager = null;
        s_EventSystem = null;
        s_UIManagerGo = null;
        s_UIs.Clear(); s_UIs = null;
        s_hideUIs.Clear(); s_hideUIs = null;
        s_WaitLoadUIs.Clear(); s_WaitLoadUIs = null;
        s_UIPoolEventQueue.Clear(); s_UIPoolEventQueue = null;
        s_IngorePauseUIs = null;
    }

    #endregion

    #region UI Camera
    /// <summary>
    /// 添加到URP的Camera内
    /// </summary>
    public static void InsterToURPCameraStack()
    {
        if(GraphicsSettings.renderPipelineAsset != null)
        {
            var cameraData = Camera.main.GetUniversalAdditionalCameraData();
            if(cameraData.cameraStack.Count > 0)
            {
                var uicamera = cameraData.cameraStack.Find(p => p.CompareTag("UICamera"));
                if(uicamera == null)
                {
                    cameraData.cameraStack.Add(GetCamera());
                }
            }
            else
            {
                cameraData.cameraStack.Add(GetCamera());
            }
        }
    }

    /// <summary>
    /// 获取指定UI框架的Camera
    /// </summary>
    /// <param name="layerKey"></param>
    /// <returns></returns>
    public static Camera GetCamera(string layerKey = null)
    {
        var data = UILayerManager.GetUILayerDataByKey(layerKey);
        return data.m_camera;
    }

    /// <summary>
    /// 将一个UI移动到另一个UICamera下
    /// </summary>
    /// <param name="ui"></param>
    /// <param name="cameraKey"></param>
    public static void ChangeUICamera(UIWindowBase ui, string layerKey)
    {
        UILayerManager.SetLayer(ui, layerKey);
    }

    /// <summary>
    /// 将一个UI重新放回它原本的UICamera下
    /// </summary>
    /// <param name="ui"></param>
    /// <param name="cameraKey"></param>
    public static void ResetUICamera(UIWindowBase ui)
    {
        UILayerManager.SetLayer(ui, ui.layerKey);
    }

    #endregion

    #region UI的打开与关闭方法
    private void CreateUIWindow(string modulePath, string UIName ,UIType uiType, UIWindowBase.WindowAnimType windowAnimType, Action<UIWindowBase> OnLoadComplete) 
    {
        string path = Utils.FormatString("{0}/{1}/{2}", modulePath, UIName, UIName);
        AssetLoadKit.LoadAsset(path, (go) =>
        {
            if (go == null) return;

            var UIbase = go.GetComponent<UIWindowBase>();
            if(UIbase == null)
            {
                Type t = Type.GetType(Utils.FormatString("{0}.{1}", AssemblyUI, UIName));
                if(t != null)
                {
                    UIbase = go.AddComponent(t) as UIWindowBase;
                }
            }
            if (UIbase == null)
            {
                Debug.LogErrorFormat("No Find UI Type: UIName:{0}", UIName);
                return;
            }

            UIbase.m_UIType = uiType;
            UIbase.windowAnimType = windowAnimType;
            UIbase.windowStatus = UIWindowBase.WindowStatus.Create;

            UISystemEvent.Dispatch(UIbase, UIEvent.OnInit);  //派发OnInit事件

            try
            {
                UIbase.Init();
            }
            catch (Exception e)
            {
                Debug.LogErrorFormat("OnInit Exception: {0},{1}", UIName, e.ToString());
            }

            UIbase.gameObject.SetActive(false);
            UILayerManager.SetLayer(UIbase);      //设置层级

            OnLoadComplete?.Invoke(UIbase);
        }, Inst.s_UIManagerGo.transform);

    }


    public static void OpenUIWindow<T>(string modulePath, UICallBack<T> callback = null) where T : UIWindowBase
    {
        OpenUIWindow(modulePath, UIType.Normal, callback);
    }
    public static void OpenUIWindow<T>(string modulePath, UIType uitype, UICallBack<T> callback = null) where T : UIWindowBase
    {
        OpenUIWindow(modulePath, uitype, UIWindowBase.WindowAnimType.PopOut, callback);
    }
    /// <summary>
    /// 打开UI
    /// </summary>
    /// <param name="UIName">UI名</param>
    /// <param name="callback">动画播放完毕回调</param>
    /// <param name="objs">回调传参</param>`
    /// <returns>返回打开的UI</returns>
    public static void OpenUIWindow<T>(string modulePath, UIType uitype, UIWindowBase.WindowAnimType windowAnimType, UICallBack<T> callback = null) where T : UIWindowBase
    {
        var UIName = typeof(T).Name;

        //检查当前图层是否已开启，避免频繁开启
        var lastUI = UIStackManager.GetLastUI(uitype);
        if (lastUI != null && lastUI.UIName == UIName)
        {
            if (uitype == UIType.Top || uitype == UIType.Fixed)
            {
                callback?.Invoke(lastUI as T);
                return;
            }
            Debug.LogWarningFormat("{0} has Open !", UIName);
            return;
        }

        //正在异步加载处理中
        if (Inst.s_WaitLoadUIs.Contains(UIName)) 
        {
            Debug.LogWarningFormat("{0} is Loading...", UIName);
            return;
        }

        var UIbase = Inst.GetHideUI(UIName);
        var UIbaseShow = Inst.GetUI(UIName);
        if (UIbase == null && UIbaseShow == null)
        {
            
            Inst.s_WaitLoadUIs.Add(UIName);
            var item = new UIPoolEvent(modulePath,UIName, uitype, windowAnimType, (ui) => {
                callback?.Invoke(ui as T);
            });
            Inst.s_UIPoolEventQueue.Enqueue(item);
        }
        else
        {
            if (UIbase == null && UIbaseShow != null) UIbase = UIbaseShow;
            UIbase.windowAnimType = windowAnimType;
            Inst.OnLoadComplete(UIbase, (ui) => {
                callback?.Invoke(ui as T);
            });
        }
  
    }

    /// <summary>
    /// 关闭UI,并不销毁UI，只是放入到Hide数组
    /// </summary>
    /// <param name="isPlayAnimAndShowLastUI">是否播放关闭动画同时在关闭后显示同层上一个隐藏的UI</param>
    /// <param name="callback">动画播放完毕回调</param>
    /// <param name="objs">回调传参</param>
    public static void CloseUIWindow<T>(bool isPlayAnimAndShowLastUI = true, UICallBack callback = null, params object[] objs) where T : UIWindowBase
    {
        CloseUIWindow(typeof(T).Name, isPlayAnimAndShowLastUI, callback, objs);
    }
    /// <summary>
    /// 关闭UI,并不销毁UI，只是放入到Hide数组
    /// </summary>
    /// <param name="UIname">目标UI</param>
    /// <param name="isPlayAnimAndShowLastUI">是否播放关闭动画同时在关闭后显示同层上一个隐藏的UI</param>
    /// <param name="callback">动画播放完毕回调</param>
    /// <param name="objs">回调传参</param>
    public static void CloseUIWindow(string UIname, bool isPlayAnimAndShowLastUI = true, UICallBack callback = null, params object[] objs)
    {
        UIWindowBase ui = Inst.GetUI(UIname);

        if (ui == null)
        {
            Debug.Log("CloseUIWindow Error UI ->" + UIname + "<-  not Exist!");
        }
        else
        {
            CloseUIWindow(Inst.GetUI(UIname), isPlayAnimAndShowLastUI, callback, objs);
        }
    }

    /// <summary>
    /// 关闭UI,并不销毁UI，只是放入到Hide数组
    /// </summary>
    /// <param name="UI">目标UI</param>
    /// <param name="isPlayAnimAndShowLastUI">是否播放关闭动画同时在关闭后显示同层上一个隐藏的UI</param>
    /// <param name="callback">动画播放完毕回调</param>
    /// <param name="objs">回调传参</param>
    public static void CloseUIWindow(UIWindowBase UI,bool isPlayAnimAndShowLastUI = true, UICallBack callback = null,params object[] objs)
    {
        Inst.RemoveUI(UI);
        UI.RemoveEventListener();

        if (isPlayAnimAndShowLastUI)
        {
            if(callback != null)
            {
                callback += CloseUIWindowCallBackPlayAnim;
            }
            else
            {
                callback = CloseUIWindowCallBackPlayAnim;
            }

            UI.windowStatus = UIWindowBase.WindowStatus.CloseAnim;
            UIAnimManager.StartExitAnim(UI, callback);
        }
        else
        {
            CloseUIWindowCallBack(UI, objs);
        }

    }

    private static void CloseUIWindowCallBackPlayAnim(UIWindowBase UI)
    {
        UI.windowStatus = UIWindowBase.WindowStatus.Close;
        UISystemEvent.Dispatch(UI, UIEvent.OnClose);  //派发OnClose事件
        try
        {
            UI.OnClose();
        }
        catch (Exception e)
        {
            Debug.LogError(UI.UIName + " OnClose Exception: " + e.ToString());
        }

        UIStackManager.RemoveUIStack(UI);
        Inst.AddHideUI(UI);
        //显示同层上一个隐藏的UI
        UIWindowBase ui = UIStackManager.GetLastUI(UI.m_UIType);
        if (ui != null)
        {
            if (ui.windowStatus == UIWindowBase.WindowStatus.Pause)
                UIAnimManager.StartEnterAnim(ui, null); //播放进入动画 
        }

    }

    private static void CloseUIWindowCallBack(UIWindowBase UI, params object[] objs)
    {
        UI.windowStatus = UIWindowBase.WindowStatus.Close;
        UISystemEvent.Dispatch(UI, UIEvent.OnClose);  //派发OnClose事件
        try
        {
            UI.OnClose();
        }
        catch (Exception e)
        {
            Debug.LogError(UI.UIName + " OnClose Exception: " + e.ToString());
        }

        UIStackManager.RemoveUIStack(UI);
        Inst.AddHideUI(UI);
    }

    /// <summary>
    /// 显示隐藏的UI
    /// </summary>
    public static UIWindowBase ShowUI(string UIname)
    {
        UIWindowBase ui = Inst.GetUI(UIname);
        return ui != null ? ShowUI(ui) : null;
    }

    /// <summary>
    /// 显示隐藏的UI
    /// </summary>
    public static UIWindowBase ShowUI(UIWindowBase ui)
    {
        ui.windowStatus = UIWindowBase.WindowStatus.Open;
        UISystemEvent.Dispatch(ui, UIEvent.OnShow);//显示事件，因为是把隐藏的UI重新激活显示
        try
        {
            ui.Show();
            ui.OnShow();
        }
        catch (Exception e)
        {
            Debug.LogError(ui.UIName + " OnShow Exception: " + e.ToString());
        }

        return ui;
    }


    /// <summary>
    /// 隐藏显示的UI
    /// </summary>
    /// <param name="UIname"></param>
    /// <returns></returns>
    public static UIWindowBase HideUI(string UIname)
    {
        UIWindowBase ui = Inst.GetUI(UIname);
        return ui != null ? HideUI(ui) : null;
    }

    public static UIWindowBase HideUI(UIWindowBase ui)
    {
        ui.windowStatus = UIWindowBase.WindowStatus.Hide;
        UISystemEvent.Dispatch(ui, UIEvent.OnHide);  //派发OnHide事件

        try
        {
            ui.Hide();
            ui.OnHide();
        }
        catch (Exception e)
        {
            Debug.LogError(ui.UIName + " OnShow Exception: " + e.ToString());
        }

        return ui;
    }


    /// <summary>
    /// 移除全部UI
    /// </summary>
    /// <param name="isPlayAnimAndShowLastUI">是否播放关闭动画同时在关闭后显示同层上一个隐藏的UI</param>
    public static void CloseAllUI(bool isPlayAnimAndShowLastUI = false)
    {
        foreach(var p in Inst.s_UIs)
        {
            var t = p.Value;
            CloseUIWindow(t, isPlayAnimAndShowLastUI);
        }
    }

    /// <summary>
    /// 销毁UI
    /// </summary>
    /// <param name="UI"></param>
    public static void DestroyUI(UIWindowBase UI)
    {
        Debug.Log("UIManager DestroyUI " + UI.name);

        if (Inst.s_hideUIs.ContainsKey(UI.UIName))
        {
            Inst.RemoveHideUI(UI);
        }
        else if (Inst.s_UIs.ContainsKey(UI.name))
        {
            Inst.RemoveUI(UI);
        }

        UIStackManager.RemoveUIStack(UI);
        
        UISystemEvent.Dispatch(UI, UIEvent.OnDestroy);  //派发OnDestroy事件
        try
        {
            UI.DestroyUI();
        }
        catch (Exception e)
        {
            Debug.LogErrorFormat("OnDestroy :{0}" , e.ToString());
        }
        Destroy(UI.gameObject);
    }

    public static void DestroyAllUI()
    {
        DestroyAllActiveUI();
        DestroyAllHideUI();
    }
    /// <summary>
    /// 删除所有打开的UI
    /// </summary>
    public static void DestroyAllActiveUI()
    {

        foreach (var uis in Inst.s_UIs.Values)
        {
            UISystemEvent.Dispatch(uis, UIEvent.OnDestroy);  //派发OnDestroy事件

            if (uis != null && uis.gameObject != null)
            {
                try
                {
                    uis.DestroyUI();
                    UIStackManager.RemoveUIStack(uis);
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("DestroyAllActiveUI : {0}, error msg:{1} ", uis.UIName, e.ToString());
                }

                Destroy(uis.gameObject);
            }
        }
        Inst.s_UIs.Clear();
    }

    /// <summary>
    /// 删除所有隐藏的UI
    /// </summary>
    public static void DestroyAllHideUI()
    {
        foreach (var uis in Inst.s_hideUIs.Values)
        {
            UISystemEvent.Dispatch(uis, UIEvent.OnDestroy);  //派发OnDestroy事件

            if(uis != null && uis.gameObject != null)
            {
                try
                {
                    uis.DestroyUI();
                    UIStackManager.RemoveUIStack(uis);
                }
                catch (Exception e)
                {
                    Debug.LogErrorFormat("DestroyAllHideUI : {0}, error msg:{1} ", uis.UIName,e.ToString());
                }

                Destroy(uis.gameObject);
            }
        }
        Inst.s_hideUIs.Clear();

    }
    #endregion

    #region api

    public static void Dispose()
    {
        Destroy(Inst.s_UIManagerGo);
    }


     T GetUI<T>() where T : UIWindowBase
    {
        return (T)GetUI(typeof(T).Name);
    }
      UIWindowBase GetUI(string UIname)
    {
        if (!s_UIs.ContainsKey(UIname))
        {
            return null;
        }
        return s_UIs[UIname];
    }

     void AddUI(UIWindowBase UI)
    {
        if (s_UIs.ContainsKey(UI.UIName))
        {
            UI.Show();
            return;
        }

        s_UIs.Add(UI.UIName, UI);

        UI.Show();
    }


     void AddHideUI(UIWindowBase UI)
    {
        if (s_hideUIs.ContainsKey(UI.UIName))
        {
            UI.Hide();
            return;
        }

        Inst.s_hideUIs.Add(UI.UIName,UI); 

        UI.Hide();
    }

      T GetHideUI<T>() where T : UIWindowBase
    {
        string UIname = typeof(T).Name;
        return (T)GetHideUI(UIname);
    }

     UIWindowBase GetHideUI(string UIname)
    {
        if(!s_hideUIs.ContainsKey(UIname))
        {
            return null;
        }
        else
        {
            return s_hideUIs[UIname];
        }
    }


     void RemoveUI(UIWindowBase UI)
    {
        if (UI == null)
        {
            throw new Exception("UIManager: RemoveUI error l_UI is null: !");
        }

        if (s_UIs.ContainsKey(UI.UIName))
        {
            s_UIs.Remove(UI.UIName);
        }

    }

     void RemoveHideUI(UIWindowBase UI)
    {
        if (UI == null)
        {
            throw new Exception("UIManager: RemoveHideUI error l_UI is null: !");
        }

        if (Inst.s_hideUIs.ContainsKey(UI.UIName))
        {
            s_hideUIs.Remove(UI.UIName);
        }


    }

    #endregion

}
